/*
 * ounit - an OPAQUE compliant framework for Computer Aided Testing
 *
 * Copyright (C) 2010-2011  Antti Andreimann
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

package com.googlecode.ounit.maven;

import java.io.File;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.descriptor.PluginDescriptor;

/**
 * Load surefire reporting plugin via reflection API to use it for parsing
 * test reports generated by surefire and failsafe plugins.
 * This requires an active Maven session and an instance of Plugin Manager
 * to work.
 * 
 * @author anttix
 *
 */
public class ReflectiveSurefireReportParser implements ReportParser {
	private Class<?> parserClass = null;
	private Class<?> testCaseClass = null;

	public ReflectiveSurefireReportParser(MavenSession session,
			BuildPluginManager pluginManager) throws Exception {
		
		// TODO: Utter a warning if surefire used in build is different from surefire used for reporting
		/*
		MavenProject project = session.getCurrentProject();
		for (Plugin p : project.getBuildPlugins()) {
			if (p.getId().startsWith("org.apache.maven.plugins:maven-surefire-plugin")) {
				System.out.println("Surefire version in project: "
						+ p.getVersion());
				System.out.println("Surefire conf: "
						+ p.getConfiguration().toString());
			}
		}
		*/

		String sfGrId = "org.apache.maven.plugins";
		String sfVer = "2.8.1";

		Plugin plugin = new Plugin();
		plugin.setGroupId(sfGrId);
		plugin.setArtifactId("maven-surefire-report-plugin");
		plugin.setVersion(sfVer);
		PluginDescriptor pluginDescriptor = pluginManager.loadPlugin(plugin,
				session.getCurrentProject().getRemotePluginRepositories(),
				session.getRepositorySession());
		parserClass = pluginManager
				.getPluginRealm(session, pluginDescriptor)
				.loadClass(
						"org.apache.maven.plugins.surefire.report.SurefireReportParser");
		testCaseClass = pluginManager
				.getPluginRealm(session, pluginDescriptor)
				.loadClass(
						"org.apache.maven.plugins.surefire.report.ReportTestCase");
	}

	/*
	 * (non-Javadoc)
	 * @see com.googlecode.ounit.maven.ReportParser#parseXMLReportFiles(java.util.List)
	 */
	public TestResults parseReportFiles(List<File> dirs) throws Exception {
		
		Object parserInstance = parserClass.getConstructor(List.class,
				Locale.class).newInstance(dirs, Locale.getDefault());
		List<?> suites = (List<?>) parserClass.getMethod("parseXMLReportFiles")
				.invoke(parserInstance);
		Map<?, ?> sum = (Map<?, ?>) parserClass.getMethod("getSummary",
				List.class).invoke(parserInstance, suites);
		List<?> failures = (List<?>) parserClass.getMethod("getFailureDetails",
				List.class).invoke(parserInstance, suites);
		
		TestResults results = new TestResults();
		
		String rv;
		
	    rv = (String)sum.get("totalTests");
	    assert rv != null: "Unable to find number of tests from test results";
    	results.setTotalTests(Integer.parseInt(rv));    	
	    rv = (String)sum.get("totalErrors");
	    assert rv != null: "Unable to find number of errors from test results";
	    results.setTotalErrors(Integer.parseInt(rv));    	
        rv = (String)sum.get("totalFailures");
	    assert rv != null: "Unable to find number of failures from test results";
	    results.setTotalFailures(Integer.parseInt(rv));    	
    	rv = (String)sum.get("totalSkipped");
	    assert rv != null: "Unable to find number of skipped tests from test results";
	    results.setTotalSkipped(Integer.parseInt(rv));
    	rv = (String)sum.get("totalElapsedTime");
	    assert rv != null: "Unable to find total elapsed time from test results";
	    results.setTotalElapsedTime(Double.parseDouble(rv));
	    
    	for(Object f: failures) {
    		Map<?, ?> failure = (Map<?, ?>)getTestCaseProperty("Failure", f);

    		String name          = (String)getTestCaseProperty("Name", f);
    		String fullClassName = (String)getTestCaseProperty("FullClassName", f);
    		String type          = (String)failure.get("type");
    		String message       = (String)failure.get("message");
		
			results.addFailure(new FailureDetail(name,
					fullClassName, type, message));
    	}
    	
		return results;
	}
	
	private Object getTestCaseProperty(String name, Object instance) throws Exception {
		return testCaseClass.getMethod("get" + name).invoke(instance);
	}
}
